package com.ruoyi.form.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.client.DistinctIterable;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.UpdateOptions;
import com.ruoyi.common.core.page.MongoPage;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.form.base.Def;
import com.ruoyi.form.base.StringUtils;
import com.ruoyi.form.service.IMongoService;
import lombok.RequiredArgsConstructor;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @Description：MongoServiceImpl
 * @Author：LZF
 * @Date：2025-06-16
 */
@RequiredArgsConstructor
@Service
public class MongoServiceImpl<T> implements IMongoService<T> {
    private final MongoTemplate mongoTemplate;

    @Override
    public void insertOrUpdate(String collectionName, Document object) {
        if (object.containsKey(Def.id) && StringUtils.isNotEmpty(object.getString(Def.id))) {
            Document document = this.selectOneById(object.getString(Def.id), collectionName);
            if (document != null && !document.isEmpty()) {
                List<Bson> bson = new ArrayList<>();
                bson.add(Filters.eq(Def.id, object.getString(Def.id)));
                Bson filter = Filters.and(bson);
                mongoTemplate.getDb().getCollection(collectionName).updateOne(filter, new Document("$set", new Document(object)));
            } else {
                mongoTemplate.getDb().getCollection(collectionName).insertOne(object);
            }
        } else {
            object.put(Def.id, IdUtils.simpleUUID());
            mongoTemplate.getDb().getCollection(collectionName).insertOne(object);
        }
    }

    @Override
    public void deleteByIds(List<String> ids, String collectionName) {
        mongoTemplate.remove(idInQuery(ids), collectionName);
    }

    @Override
    public void deleteByFilter(Bson filter, String collectionName) {
        mongoTemplate.getDb().getCollection(collectionName).deleteMany(filter);
    }

    @Override
    public void saveOrUpdateOne(String collectionName, Bson filter, Map<String, Object> object) {

        mongoTemplate.getDb().getCollection(collectionName).updateOne(filter,
                new Document("$set", new Document(object)));

    }

    @Override
    public Document findOneById(String id, Class<T> clazz, String collectionName) {
        T t = mongoTemplate.findById(id, clazz, collectionName);
        if (t == null) {
            return new Document();
        } else {
            return toDocument(t);
        }
    }

    @Override
    public Document selectOneById(String id, String collectionName) {
        List<Bson> bson = new ArrayList<>();
        bson.add(Filters.eq(Def.id, id));
        Bson filter = Filters.and(bson);
        Iterable<Document> documents = mongoTemplate.getDb().getCollection(collectionName).find(filter);
        Iterator<Document> it = documents.iterator();
        while (it.hasNext()) {
            return it.next();
        }
        return new Document();
    }

    @Override
    public MongoPage find(String collectionName, Bson filter, Bson sort, MongoPage page) {
        MongoCollection<Document> mongoCollection = mongoTemplate.getDb().getCollection(collectionName);
        long total = mongoCollection.countDocuments(filter);
        FindIterable<Document> data = mongoCollection.find(filter).sort(sort);
        if (page.getPageSize() > 0) {
            data = data.skip((page.getPageNum() - 1) * page.getPageSize())
                    .limit(page.getPageSize());
        }
        List<Object> data_ = new ArrayList<Object>();
        MongoCursor cursor = data.iterator();
        while (cursor.hasNext()) {
            data_.add(cursor.next());
        }
        page.setTotal(total);
        page.setList(data_);
        return page;
    }

    /**
     * mongo查询list数据
     * @param collectionName
     * @param filter
     * @param sort
     * @return
     */
    public List<Document> find(String collectionName, Bson filter, Bson sort) {
        FindIterable<Document> data = mongoTemplate.getDb().getCollection(collectionName).find(filter)
                .sort(sort);
        List<Document> data_ = new ArrayList<Document>();
        MongoCursor<Document> cursor = data.iterator();
        Document dataTmp = null;
        while (cursor.hasNext()) {
            dataTmp = cursor.next();
            dataTmp.put("id", dataTmp.get(Def.id));
            data_.add(dataTmp);
        }
        return data_;
    }


    private Query idInQuery(Collection<? extends Serializable> idList) {
        Criteria criteria = Criteria.where(Def.id).in(idList);
        return Query.query(criteria);
    }

    private static <T> Pair<Query, Update> getPair(T t) throws JsonProcessingException {
        Query query = new Query();
        Update update = new Update();
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(t);
        Document document = objectMapper.readValue(json, Document.class);
        // 删除key为“_id”的键值对，同时返回value
        Object id = document.remove(Def.id);
        // 指定查询字段
        query.addCriteria(Criteria.where(Def.id).is(id));
        // 指定更新字段
        document.forEach(update::set);
        // 返回Pair对象
        return Pair.of(query, update);
    }

    public static <T> Document toDocument(T object){
        String json = JSONObject.toJSONString(object);
        Document document = Document.parse(json);
        return document;
    }
}
